/***************************************************************************
 *	@file 		fetion_database.cpp
 *	@Author 	baoyu0125@gmail.com
 *	@create		2011-7
 *	@version 	1.0.0
 *	@brief		definition of functions about database operation
 *	@modify		none
 ***************************************************************************/

#include "fetion_database.h"
#include "fetion_util.h"
#include "fetion_exception.h"

/*--------------------------------------------------------------------
 * @brief : open sqlite3 database
 * @param db_file : database file path
 * @returns : whether open successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::Open(std::string const& db_file)
{
	if(db_file.empty()) {
		return false;
	}
	if(sqlite3_open(db_file.c_str(), &db_) != SQLITE_OK) {
		return false;
  	}
	return true;
} 

/*--------------------------------------------------------------------
 * @brief : execute a select SQL statement
 * @param stmt : select SQL statement
 * @param res : query result
 * @returns : whether query successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::SelectStmt(std::string const& stmt, ResultTable& res)
{
	char *errmsg;
	int   ret;

	res.reset();

	ret = sqlite3_exec(db_, stmt.c_str(), SelectCallback, static_cast<void*>(&res), &errmsg);

	if (ret != SQLITE_OK) {
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : callback of sqlite3_exec()
 * @param p_data : the 4th parameter of sqlite3_exec, to store query result
 * @param num_fields :  the number of columns in the result
 * @param p_fields : string of each column query result
 * @param p_col_names : column names of query result
 * @returns : return state(0 for success)
 *-------------------------------------------------------------------*/
int SQLiteWrapper::SelectCallback(void *p_data, int num_fields, char **p_fields, char** p_col_names)
{
	ResultTable* res = reinterpret_cast<ResultTable*>(p_data);
	ResultRecord record;

#ifdef SQLITE_WRAPPER_REPORT_COLUMN_NAMES
	// Hubert Castelain: column names in the first row of res if res is empty
	if(res->records_.size()==0) {
		ResultRecord col_names;
		for(int i=0; i < num_fields; i++) {
			if(p_fields[i]) 
				col_names.fields_.push_back(p_col_names[i]);
			else
				col_names.fields_.push_back("(null)"); // or what else ?
		}
		res->records_.push_back(col_names);
	}
#endif

	for(int i=0; i < num_fields; i++) {
		// Hubert Castelain: special treatment if null
		if(p_fields[i]) 
			record.fields_.push_back(p_fields[i]);
		else
			record.fields_.push_back("<null>");
	}

	res->records_.push_back(record);
	return 0;
}

/*--------------------------------------------------------------------
 * @brief : create a SQL statement object
 * @param statement : SQL statement
 * @returns : pointer of SQL statement object
 *-------------------------------------------------------------------*/
SQLiteStatement* SQLiteWrapper::Statement(std::string const& statement)
{
	SQLiteStatement* stmt;
	try {
		// SQLiteStatement object can only be created by SQLiteWrapper object
		stmt = new SQLiteStatement(statement, db_);
		return stmt;
	}
	catch (const char* e) {
		return 0;
	}
}

/*--------------------------------------------------------------------
 * @brief : constructor of SQLiteStatement
 * @param statement : SQL statement
 * @param db : sqlite3 database object
 * @returns : none
 *-------------------------------------------------------------------*/
SQLiteStatement::SQLiteStatement(std::string const& statement, sqlite3* db)
{
	if (sqlite3_prepare(db, statement.c_str(),  // get byte code of SQL statement
		-1,					// If than zero, then stmt is read up to the first nul terminator
		&stmt_, 0			// Pointer to unused portion of stmt
		) != SQLITE_OK) {
		throw sqlite3_errmsg(db);
	}

	if (!stmt_) {
		throw "stmt_ is 0";
	}
}

/*--------------------------------------------------------------------
 * @brief : destructor of SQLiteStatement
 * @returns : none
 *-------------------------------------------------------------------*/
SQLiteStatement::~SQLiteStatement()
{
	// Prevent the database remaining locked after some statement.
	// syntax: int sqlite3_finalize(sqlite3_stmt *pStmt);
	if(stmt_) sqlite3_finalize(stmt_);
}

/*--------------------------------------------------------------------
 * @brief : bind string to SQL statement
 * @param pos_zero_indexed : Index of wildcard
 * @param value : bind value
 * @returns : whether bind successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::Bind(int pos_zero_indexed, std::string const& value)
{
	if (sqlite3_bind_text(stmt_, pos_zero_indexed+1,  // Index of wildcard
			value.c_str(), value.length(),      // length of text
			SQLITE_TRANSIENT     // SQLITE_TRANSIENT: SQLite makes its own copy
		) != SQLITE_OK) {
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : bind double to SQL statement
 * @param pos_zero_indexed : Index of wildcard
 * @param value : bind value
 * @returns : whether bind successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::Bind(int pos_zero_indexed, double value)
{
	if (sqlite3_bind_double(
			stmt_,
			pos_zero_indexed+1,  // Index of wildcard
			value
		) != SQLITE_OK) {
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : bind integer to SQL statement
 * @param pos_zero_indexed : Index of wildcard
 * @param value : bind value
 * @returns : whether bind successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::Bind(int pos_zero_indexed, int value)
{
	if (sqlite3_bind_int(
			stmt_,
			pos_zero_indexed+1,  // Index of wildcard
			value 
		) != SQLITE_OK) {
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : bind nothing to SQL statement
 * @param pos_zero_indexed : Index of wildcard
 * @returns : whether bind successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::BindNull(int pos_zero_indexed)
{
	if (sqlite3_bind_null(
			stmt_,
			pos_zero_indexed+1  // Index of wildcard
		) != SQLITE_OK) {
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : execute SQL by stmt_
 * @returns : whether execute successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::Execute()
{
	int rc = sqlite3_step(stmt_);	// execute the byte code of SQL statement
	if (rc == SQLITE_BUSY) return false;
	if (rc == SQLITE_ERROR) return false;
	if (rc == SQLITE_MISUSE) return false;
	if (rc != SQLITE_DONE) return false;
	sqlite3_reset(stmt_);
	return true;
}

SQLiteStatement::dataType SQLiteStatement::DataType(int pos_zero_indexed)
{
	return dataType(sqlite3_column_type(stmt_, pos_zero_indexed));
}

int SQLiteStatement::ValueInt(int pos_zero_indexed)
{
	return sqlite3_column_int(stmt_, pos_zero_indexed);
}

std::string SQLiteStatement::ValueString(int pos_zero_indexed)
{
	return std::string(reinterpret_cast<const char*>(sqlite3_column_text(stmt_, pos_zero_indexed)));
}

bool SQLiteStatement::RestartSelect()
{
	sqlite3_reset(stmt_);
	return true;
}

/*--------------------------------------------------------------------
 * @brief : cleaning up which not depending on NextRow()
 * @returns : whether reset successfully
 *-------------------------------------------------------------------*/
bool SQLiteStatement::Reset()
{
	int rc = sqlite3_step(stmt_);

	sqlite3_reset(stmt_);

	if (rc == SQLITE_ROW) return true;
	return false;
}

/*--------------------------------------------------------------------
 * @brief : get next row of query result
 * @param src_str : source string to construct programString
 * @returns : whether finish query
 *-------------------------------------------------------------------*/
bool SQLiteStatement::NextRow()
{
	bool ret = true;
	do {
		int rc = sqlite3_step(stmt_);
		
		// the SQL statement being executed returns any data
		// in this condition, sqlite3_reset is not necessary
		if (SQLITE_ROW == rc) {	
			break;
		}
		// the statement has finished executing successfully
		// but return no result
		if (SQLITE_DONE == rc) { 
			sqlite3_reset(stmt_);
			ret = false;
			break;
		} 
		// the this routine was called inappropriately. 
		// Perhaps it was called on a prepared statement that has already been finalized
		// or on one that had previously returned SQLITE_ERROR or SQLITE_DONE. 
		else if (rc == SQLITE_MISUSE) {
			ret = false;
			throw fetionException("SQLiteStatement::NextRow SQLITE_MISUSE");
		} 
		// the database engine was unable to acquire the database locks it needs to do its job
		else if (rc == SQLITE_BUSY  ) {
			ret = false;
			throw fetionException("SQLiteStatement::NextRow SQLITE_BUSY");
		} 
		// a run-time error (such as a constraint violation) has occurred
		else if (rc == SQLITE_ERROR ) {
			ret = false;
			throw fetionException("SQLiteStatement::NextRow SQLITE_ERROR");
		}
	}while(false);
	
	return ret;
}

/*--------------------------------------------------------------------
 * @brief : directly execute a SQL statement
 * @param stmt : SQL statement to be executed
 * @returns : whether executed successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::DirectStatement(std::string const& stmt)
{
	char *errmsg;
	int   ret;

	ret = sqlite3_exec(db_, stmt.c_str(), 0, 0, &errmsg);

	if(ret != SQLITE_OK) {
		// throw errmsg;
		return false;
	}
	return true;
}

/*--------------------------------------------------------------------
 * @brief : return last error string
 * @returns : last error string
 *-------------------------------------------------------------------*/
std::string SQLiteWrapper::LastError()
{
 	return sqlite3_errmsg(db_);
}

/*--------------------------------------------------------------------
 * @brief : begin of Transaction
 * @returns : whether executed successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::Begin()
{
	return DirectStatement("begin");
}

bool SQLiteWrapper::End()
{
	return DirectStatement("end");
}

/*--------------------------------------------------------------------
 * @brief : commit of Transaction
 * @returns : whether executed successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::Commit()
{
	return DirectStatement("commit");
}

/*--------------------------------------------------------------------
 * @brief : rollback of Transaction
 * @returns : whether executed successfully
 *-------------------------------------------------------------------*/
bool SQLiteWrapper::Rollback()
{
	return DirectStatement("rollback");
}
